#include "stdafx.h"
#include "DBFRecordset.h"
#include "DBFException.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// CDBFRecordset
IMPLEMENT_DYNAMIC(CDBFRecordset, CObject )

CDBFRecordset::CDBFRecordset(CDBFTableDef* pDBFTable)
{
	m_pTableDef = NULL;

	m_nCurTableRecN = -1;
	m_bBOF = TRUE;			
	m_bEOF = TRUE;

	m_pData = NULL;

	m_nLockMode = DbfUtility::LockPessimistic;
	m_nLockType = DbfUtility::NoLock;

	m_nEditMode = DbfUtility::noMode;
}

CDBFRecordset::~CDBFRecordset()
{
	if (IsOpen())
		Close();
}

BOOL CDBFRecordset::Open(LPCTSTR lpszFileName, UINT nOpenFlag)
{
	CString strFileName;

	if(lpszFileName == NULL)
		strFileName = GetDefaultDBName();
	else
		strFileName = lpszFileName;

	if (strFileName.GetLength() < 4)
		return FALSE;

	// Re-Opening is invalid.
	if (IsOpen())
	{
		return FALSE;
	}

	m_pTableDef = new CDBFTableDef;
	m_pTableDef->m_bOpen = FALSE;

	m_pTableDef->Open(strFileName, nOpenFlag);

	if(!IsOpen())
		return FALSE;

	m_DBFFields = m_pTableDef->m_DBFFields;

	if(m_pTableDef->GetRecordCount() > 0)
		MoveFirst();
	else
		m_nCurTableRecN = 0;
	
	return m_pTableDef->m_bOpen;
}

void CDBFRecordset::Close()
{
	

	if (IsOpen())
		m_pTableDef->Close();

	m_nEditMode = DbfUtility::noMode;

	m_nCurTableRecN = -1;
	m_bBOF = TRUE;			
	m_bEOF = TRUE;

	if(m_pTableDef != NULL)
	{
		delete m_pTableDef;
		m_pTableDef = NULL;
		m_DBFFields.m_pFields = NULL;
		m_DBFFields.m_pCurRec = NULL;
	}

	if(m_pData != NULL)
	{
		delete[] m_pData;
		m_pData = NULL;
	}
}

CString CDBFRecordset::GetDefaultDBName()
{
	

	// Override and add UNC path to .DBF file
	return _T("");
}

BOOL CDBFRecordset::IsBOF() const
{
	

	return m_bBOF;
}

BOOL CDBFRecordset::IsEOF() const
{
	

	return m_bEOF;
}

BOOL CDBFRecordset::IsDeleted() const
{
	assert(IsOpen());
	return (m_DBFFields.m_pCurRec[0] == REC_FLAG_DELETED);
}

CTDbfString CDBFRecordset::GetName()
{
	assert(IsOpen());
	return m_pTableDef->GetName();
}

CTDbfString CDBFRecordset::GetDBFilePath()
{
	assert(IsOpen());
	return m_pTableDef->GetDBFilePath();
}

CTime CDBFRecordset::GetDateCreated()
{
	assert(IsOpen());
	return m_pTableDef->GetDateCreated();
}

CTime CDBFRecordset::GetDateLastUpdated()
{
	assert(IsOpen());
	return m_pTableDef->GetDateLastUpdated();
}

long CDBFRecordset::GetRecordCount()
{
	assert(IsOpen());
	return m_pTableDef->GetRecordCount();
}

short CDBFRecordset::GetFieldCount()
{
	assert(IsOpen());
	return m_DBFFields.GetFieldCount();
}

long CDBFRecordset::GetAbsolutePosition()
{
	assert(IsOpen());
	return m_nCurTableRecN;
}

void CDBFRecordset::SetAbsolutePosition(long lPosition)
{
	assert(IsOpen());
	if (lPosition<0 || lPosition>=GetRecordCount())
		return;
	DBF_LONG lTableRecN = lPosition; 

	// Call Move.
	m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
	m_nCurTableRecN = lTableRecN;
	m_bBOF = m_bEOF = FALSE;

}

void CDBFRecordset::MoveNext()
{
	if (IsEOF())
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lTableRecN = m_nCurTableRecN;
	
	lTableRecN++;

	if(lTableRecN < GetRecordCount())
	{
		// Call Move.
		m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
		m_nCurTableRecN = lTableRecN;
		m_bBOF = m_bEOF = FALSE;
	}
	else
	{
		m_bEOF = TRUE;
		m_bBOF = GetRecordCount() > 0; 
	}
	
}

void CDBFRecordset::MovePrev()
{
	if (IsBOF())
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lTableRecN = m_nCurTableRecN; 

	lTableRecN--;

	if(lTableRecN >= 0)
	{
		// Call Move.
		m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
		m_nCurTableRecN = lTableRecN;
		m_bBOF = m_bEOF = FALSE;
	}
	else
	{
		m_bBOF = TRUE;
		m_bEOF = GetRecordCount() > 0; 
	}
}

void CDBFRecordset::MoveFirst()
{
	if (GetRecordCount() < 1)
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lTableRecN = 0; 

	// Call Move.
	m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
	m_nCurTableRecN = lTableRecN;
	m_bBOF = m_bEOF = FALSE;
	
}

void CDBFRecordset::MoveLast()
{
	if (GetRecordCount() < 1)
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lTableRecN = GetRecordCount() - 1; 

	// Call Move.
	m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
	m_nCurTableRecN = lTableRecN;
	m_bBOF = m_bEOF = FALSE;
}

void CDBFRecordset::Move(long lOffsetRec)
{
	if (GetRecordCount() < 1)
		return;
	m_nEditMode = DbfUtility::noMode;

	DBF_LONG lTableRecN = /*m_nCurTableRecN + */lOffsetRec; 

	if(lOffsetRec < 0)
	{
		if(lTableRecN < 0)
		{
			lTableRecN = 0;
			m_bBOF = TRUE;
		}
		else
			m_bBOF = FALSE;

		m_bEOF = FALSE; 
	}
	else if(lOffsetRec > 0)
	{
		if(lTableRecN >= GetRecordCount())
		{
			lTableRecN = GetRecordCount() - 1;
			m_bEOF = TRUE;
		}
		else
			m_bEOF = FALSE;

		m_bBOF = FALSE; 
	}

	// Call Move.
	m_pTableDef->ReadRecord(lTableRecN, m_DBFFields.m_pCurRec);
	m_nCurTableRecN = lTableRecN;
}

void CDBFRecordset::CancelUpdate()
{
	assert(IsOpen());
	if (m_nEditMode == DbfUtility::noMode)
		return;

	m_pTableDef->ReadRecord(m_nCurTableRecN, m_DBFFields.m_pCurRec);
	m_nEditMode = DbfUtility::noMode;
}

BOOL CDBFRecordset::LockRecord()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode)
		return FALSE;
	if(!(m_nOpenType & TG_DBF_USE_EXCLUSIVE))
	{
		switch(m_nLockType)
		{
		case DbfUtility::LockDBase:
			{
				// 0xEFFFFFFFL - rec number
				QWORD offset = 0xEFFFFFFFL - m_nCurTableRecN;
				QWORD range = 1;
				return::LockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		case DbfUtility::LockClipper:
			{
				// 0x40000000L + rec number
				QWORD offset = 1000000000 + (m_nCurTableRecN + 1);
				QWORD range = 1;
				return::LockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		case DbfUtility::LockFoxpro:
			{
				// 0x40000000L + rec offset
				QWORD offset = 0x40000000L +/* m_pTableDef->m_DBFHead.data_offset +*/ m_pTableDef->m_DBFHead.rec_size * m_nCurTableRecN;
				QWORD range = 1;
				return::LockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		}
	}
	return TRUE;
}

BOOL CDBFRecordset::UnlockRecord()
{
	if (IsBOF() || IsEOF() || m_nEditMode != DbfUtility::noMode)
		return FALSE;

	if(!(m_nOpenType & TG_DBF_USE_EXCLUSIVE))
	{
		switch(m_nLockType)
		{
		case DbfUtility::LockDBase:
			{
				// 0xEFFFFFFFL - rec number
				QWORD offset = 0xEFFFFFFFL - m_nCurTableRecN;
				QWORD range = 1;
				return::UnlockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		case DbfUtility::LockClipper:
			{
				// 0x40000000L + rec number
				QWORD offset = 1000000000 + (m_nCurTableRecN + 1);
				QWORD range = 1;
				return::UnlockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		case DbfUtility::LockFoxpro:
			{
				// 0x40000000L + rec offset
				QWORD offset = 0x40000000L +/* m_pTableDef->m_DBFHead.data_offset +*/ m_pTableDef->m_DBFHead.rec_size * m_nCurTableRecN;
				QWORD range = 1;
				return::UnlockFile((HANDLE)m_pTableDef->m_DBFile.m_hFile, LODWORD(offset), HIDWORD(offset), LODWORD(range), HIDWORD(range));
			}
		}
	}
	return TRUE;
}

void CDBFRecordset::AddNew()
{
	assert(IsOpen());
	m_nEditMode = DbfUtility::addnew;

	memset(m_DBFFields.m_pCurRec, 0, m_pTableDef->m_DBFHead.rec_size);

	COleVariant varEmpty;
	varEmpty.Clear();

	for(short i = 0; i < m_DBFFields.GetFieldCount(); i++)
	{
		FIELD_REC* pFieldRec = m_DBFFields.GetFieldRec(i);
		TGSetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varEmpty);
	}

	m_DBFFields.m_pCurRec[0] = REC_FLAG_NORMAL;
}

void CDBFRecordset::Edit()
{
	assert(IsOpen());
	m_nEditMode = DbfUtility::edit;
}

void CDBFRecordset::Update()
{
	assert(IsOpen());
	if (m_nEditMode == DbfUtility::noMode)
		return;

	switch(m_nEditMode)
	{
	case DbfUtility::edit:
		{
			m_pTableDef->ReadHeader();
			m_pTableDef->WriteRecord(m_nCurTableRecN, m_DBFFields.m_pCurRec);
//			m_pTableDef->WriteHeader();
			break;
		}
	case DbfUtility::addnew:
		{
			m_pTableDef->ReadHeader();
			m_pTableDef->m_DBFHead.last_rec++;
			m_nCurTableRecN = max(0,GetRecordCount() - 1);
			m_pTableDef->WriteRecord(m_nCurTableRecN, m_DBFFields.m_pCurRec);
			m_pTableDef->WriteHeader();
			m_bBOF = m_bEOF = FALSE;
			break;
		}
	}

	m_nEditMode = DbfUtility::noMode;
}

void CDBFRecordset::Delete()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode)
		return;

	m_DBFFields.m_pCurRec[0] = REC_FLAG_DELETED;
	m_pTableDef->WriteRecord(m_nCurTableRecN, m_DBFFields.m_pCurRec);
}

void CDBFRecordset::Undelete()
{
	assert(IsOpen());
	if (m_nEditMode != DbfUtility::noMode)
		return;
	m_DBFFields.m_pCurRec[0] = REC_FLAG_NORMAL;
	m_pTableDef->WriteRecord(m_nCurTableRecN, m_DBFFields.m_pCurRec);
}

COleVariant CDBFRecordset::GetFieldValue(LPCTSTR lpszName)
{
	COleVariant varValue;
	CTDbfString sKey(lpszName);
	GetFieldValue(sKey.toLPCSTR(), varValue);

	return varValue;
}

COleVariant CDBFRecordset::GetFieldValue(int nIndex)
{
	COleVariant varValue;

	GetFieldValue(nIndex, varValue);

	return varValue;
}

long CDBFRecordset::GetFieldValue(const char* lpszName, COleVariant& varValue)
{
	for(short nIndex = 0;
		_stricmp(m_DBFFields.m_pFields[nIndex].field_name, lpszName) != 0 &&
		nIndex <= m_DBFFields.m_nFieldCount;
		nIndex++)
		{};
	
	return GetFieldValue(nIndex, varValue);
}

long CDBFRecordset::GetFieldValue(LPCTSTR lpszName, CString& sVal)
{
	COleVariant varValue;
	CTDbfString sKey(lpszName);
	long nRes = GetFieldValue(sKey.toLPCSTR(), varValue);
	CTDbfString sTmp;
	FormatString(varValue,sTmp);
	sVal = sTmp.toCString();
	return nRes;
}

long CDBFRecordset::GetFieldValue(LPCTSTR lpszName,double& dVal)
{
	COleVariant varValue;
	CTDbfString sKey(lpszName);
	long nRes = GetFieldValue(sKey.toLPCSTR(), varValue);
	
	CTDbfString sVal;
	FormatString(varValue,sVal);
	dVal = atof(sVal.toLPCSTR());

	return nRes;
}

long CDBFRecordset::GetFieldValue(LPCTSTR lpszName,int& nVal)
{
	COleVariant varValue;
	CTDbfString sKey(lpszName);
	long nRes = GetFieldValue(sKey.toLPCSTR(), varValue);
	
	CTDbfString sVal;
	FormatString(varValue,sVal);
	nVal = atoi(sVal.toLPCSTR());

	return nRes;
}

long CDBFRecordset::GetFieldValue(int nIndex,CTDbfString& sVal)
{
	COleVariant varValue;
	long nRes = GetFieldValue(nIndex, varValue);
	
	FormatString(varValue,sVal);

	return nRes;
}

long CDBFRecordset::GetFieldValue(int nIndex,double& dVal)
{
	COleVariant varValue;
	long nRes = GetFieldValue(nIndex, varValue);
	
	CTDbfString sVal;
	FormatString(varValue,sVal);
	dVal = atof(sVal.toLPCSTR());

	return nRes;
}

long CDBFRecordset::GetFieldValue(int nIndex,int& nVal)
{
	COleVariant varValue;
	long nRes = GetFieldValue(nIndex, varValue);
	
	CTDbfString sVal;
	FormatString(varValue,sVal);
	nVal = atoi(sVal.toLPCSTR());

	return nRes;
}

long CDBFRecordset::GetFieldValue(int nIndex, COleVariant& varValue)
{
	assert(IsOpen());
	if (nIndex > m_DBFFields.m_nFieldCount)
		return 0;

	FIELD_REC* pFieldRec = m_DBFFields.GetFieldRec(nIndex);

	long lLen = TGGetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varValue);
	switch(pFieldRec->field_type)
	{
	case FLD_TYPE_MEMO:
		{
			if(varValue.vt == VT_UI4)
				if(varValue.ulVal > 0)
				{
					CTDbfString strValue;
					m_pTableDef->ReadMemoRecord(varValue.ulVal, lLen, strValue);
					varValue.Clear();
					varValue = strValue.toCString();
				}
				else
				{
					lLen = 0;
					varValue.Clear();
					varValue.vt = VT_EMPTY;
				}
			else if(varValue.vt == VT_DECIMAL) // SIX
				if(varValue.decVal.Lo32 > 0)
				{
					CTDbfString strValue;
					lLen = varValue.decVal.Hi32;
					m_pTableDef->ReadMemoRecord(varValue.ulVal, lLen, strValue);
					varValue.Clear();
					varValue = strValue.toCString();
				}
				else
				{
					lLen = 0;
					varValue.Clear();
					varValue.vt = VT_EMPTY;
				}
			break;
		}
	case FLD_TYPE_DOUBLE:
		{
			if(pFieldRec->len_info.num_size.len == sizeof(DBF_DOUBLE))
				break;
		}
	case FLD_TYPE_GENERAL:
	case FLD_TYPE_PICTURE:
		{
			if(m_pData != NULL)	{ delete[] m_pData;	m_pData = NULL;	}
			if(varValue.ulVal > 0)
			{
				m_pTableDef->ReadMemoRecord(varValue.ulVal, lLen, &m_pData);
				varValue.vt = VT_BYREF|VT_UI1; 
				varValue.pbVal = m_pData;
			}
			else
			{
				lLen = 0;
				varValue.Clear();
				varValue.vt = VT_EMPTY;
			}
			break;
		}
	}
	return lLen;
}

long CDBFRecordset::GetFieldValue( LPCTSTR lpszName,float& dVal )
{
	double val = 0.;
	long result = GetFieldValue(lpszName, val);
	dVal = (float)val;
	return result;
}
void CDBFRecordset::SetFieldValue(const char* lpszName,	const COleVariant& varValue)
{
	for(short nIndex = 0;
		_stricmp(m_DBFFields.m_pFields[nIndex].field_name, lpszName) != 0 &&
		nIndex <= m_DBFFields.m_nFieldCount;
		nIndex++)
		{};

	SetFieldValue(nIndex, varValue);
}

void CDBFRecordset::SetFieldValue(LPCTSTR lpszName,LPCTSTR pszVal)
{
	if (pszVal == NULL) return;
	CTDbfString sVal(pszVal);
	CTDbfString sKey(lpszName);
	COleVariant varValue;
	varValue.vt = VT_BSTR;
	varValue.bstrVal = sVal.toBSTR();
	SetFieldValue(sKey.toLPCSTR(),varValue);
	SysFreeString(varValue.bstrVal);
}

void CDBFRecordset::SetFieldValue(LPCTSTR lpszName,double dVal)
{
	COleVariant varValue;
	varValue.vt = VT_R8;
	varValue.dblVal = dVal;
	CTDbfString sKey(lpszName);
	SetFieldValue(sKey.toLPCSTR(),varValue);
}

void CDBFRecordset::SetFieldValue(LPCTSTR lpszName,int nVal)
{
	COleVariant varValue;
	varValue.vt = VT_I4;
	varValue.lVal = nVal;
	CTDbfString sKey(lpszName);
	SetFieldValue(sKey.toLPCSTR(),varValue);
}

void CDBFRecordset::SetFieldValue(int nIndex,const char* pszVal)
{
	if (pszVal == NULL) return;
	CTDbfString sVal(pszVal);
	COleVariant varValue;
	varValue.vt = VT_BSTR;
	varValue.bstrVal = sVal.toBSTR();
	SetFieldValue(nIndex,varValue);
	SysFreeString(varValue.bstrVal);
}

void CDBFRecordset::SetFieldValue(int nIndex,double dVal)
{
	COleVariant varValue;
	varValue.vt = VT_R8;
	varValue.dblVal = dVal;
	SetFieldValue(nIndex,varValue);
}

void CDBFRecordset::SetFieldValue(int nIndex,int nVal)
{
	COleVariant varValue;
	varValue.vt = VT_I4;
	varValue.lVal = nVal;
	SetFieldValue(nIndex,varValue);
}

void CDBFRecordset::SetFieldValue(int nIndex, const COleVariant& varValue)
{
	assert(IsOpen());
	if (nIndex > m_DBFFields.m_nFieldCount)
		return;

	FIELD_REC* pFieldRec = m_DBFFields.GetFieldRec(nIndex);

	switch(pFieldRec->field_type)
	{
	case FLD_TYPE_MEMO:
		{
			COleVariant varDBFValue;
			if(varValue.vt == VT_BSTR)
			{
				CTDbfString strValue = varValue.bstrVal;
				DBF_CHAR*  pCurRec = new DBF_CHAR[m_pTableDef->m_DBFHead.rec_size];

				TGGetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varDBFValue);

				if(varDBFValue.vt == VT_UI4)
				{
					DBF_LONG lLen = strValue.GetLength();
					m_pTableDef->WriteMemoRecord(varDBFValue.ulVal, lLen, strValue);
				}
				else if(varDBFValue.vt == VT_DECIMAL) // SIX
				{
					DBF_LONG lLen = varDBFValue.decVal.Hi32 > 0 ? varDBFValue.decVal.Hi32 : strValue.GetLength();
					m_pTableDef->WriteMemoRecord(varDBFValue.ulVal, lLen, strValue);
					varDBFValue.decVal.Hi32 = lLen;
				}

				delete[] pCurRec;

				TGSetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varDBFValue);
			}
			break;
		}
	case FLD_TYPE_DOUBLE:
		{
			if(pFieldRec->len_info.num_size.len == sizeof(DBF_DOUBLE))
			{
				TGSetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varValue);
				break;
			}
		}
	case FLD_TYPE_GENERAL:
	case FLD_TYPE_PICTURE:
		{
			break;
		}
	default:
		TGSetFieldValue(m_DBFFields.m_pCurRec, pFieldRec, varValue);
	}
}

BOOL CDBFRecordset::IsOpen() const
{
	
	return (m_pTableDef == NULL ? FALSE : m_pTableDef->m_bOpen);
}

void CDBFRecordset::ThrowDBFException(long nTGDBFError)
{
	

	TGThrowDBFException(nTGDBFError);
}

#ifdef _DEBUG
void CDBFRecordset::AssertValid() const
{
	CObject::AssertValid();
}

void CDBFRecordset::Dump(CDumpContext& dc) const
{
	

	CObject::Dump(dc);

	dc << "\nm_nOpenType = " << m_nOpenType;
	dc << "\nm_nEditMode = " << m_nEditMode;
	dc << "\nm_nCurTableRecN = " << m_nCurTableRecN;
	dc << "\nm_bBOF = " << m_bBOF;			
	dc << "\nm_bEOF = " << m_bEOF;			

	dc << "\n";
}
#endif //_DEBUG

BOOL CDBFRecordset::ChangeOpenFlag(UINT nNewFlag)
{
	assert(m_pTableDef);
	return m_pTableDef->ChangeOpenFlag(nNewFlag);
}

BOOL CDBFRecordset::FormatString(const COleVariant& varValue,CTDbfString& Val)
{
	CString sVal(_T(""));
	switch(varValue.vt)
	{
	case VT_I4:
		{
			sVal.Format(_T("%d"),varValue.lVal);
			break;
		}
	case VT_UI1:
		{
			sVal.Format(_T("%d"),varValue.bVal);
			break;
		}
	case VT_I2:
		{
			sVal.Format(_T("%d"),varValue.iVal);
			break;
		}
	case VT_R4:
		{
			sVal.Format(_T("%f"),varValue.fltVal);
			break;
		}
	case VT_R8:
		{
			sVal.Format(_T("%g"),varValue.dblVal);
			break;
		}
	case VT_BSTR:
		{
			sVal = varValue.bstrVal;
			break;
		}
	case VT_UI2:
		{
			sVal.Format(_T("%d"),varValue.uiVal);
			break;
		}
	case VT_UI4:
		{
			sVal.Format(_T("%d"),varValue.ulVal);
			break;
		}
	case VT_INT:
		{
			sVal.Format(_T("%d"),varValue.intVal);
			break;
		}
	case VT_UINT:
		{
			sVal.Format(_T("%d"),varValue.uintVal);
		}
	default:
		break;
	}
	Val = sVal;
	return !sVal.IsEmpty();
}